#include "XmlParser.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/SAX.h>
#include <libxml/xmlstring.h>
#include <memory>
#include <stack>
#include <iostream>
#include <assert.h>
#include <Windows.h>
namespace tinkle{

//#define  XMLPARSER_CPP_DEBUG

	XmlParser::XmlParser() : _c_txt(NULL), _node(NULL), _root(NULL){
		//set sax handler
		memset(&_sax_hdl, 0, sizeof(_sax_hdl));
		xmlSAX2InitDefaultSAXHandler(&_sax_hdl, 0);		
		xmlSAXVersion(&_sax_hdl, 1);
		_sax_hdl.startElement =	On_Start_Element;
		_sax_hdl.endElement = On_End_Element;
		_sax_hdl.characters = On_Characters;
		_sax_hdl.error = On_Error;
		_sax_hdl.warning = On_Warning;
		_sax_hdl.cdataBlock = On_CdataBlock;

		UnregisterCallbackFunc();		
		Refresh();
	}
	
	XmlParser::~XmlParser() {	
		_clear();
		if (_c_txt) {
			xmlFreeParserCtxt(_c_txt);
		}
	}

	int XmlParser::Refresh() {       
        
        _need_to_clear = true;
		_badparser = false;
		return PARSER_NO_ERROR;
	}

	void XmlParser::_clear() {
		_need_to_clear = false;
		if (_c_txt) {			
			//xmlCtxtReset(_c_txt);
			xmlFreeParserCtxt(_c_txt);
			_c_txt = NULL;
		}
		if (_node) {
			if (!_node_send_out)
				delete _node;
			_node = NULL;						
		}			
		_node_send_out = false;
		if (_root)  {
			delete _root;
			_root = NULL;
		}
		_stack_clear();
		_depth = 0;
	}

	int XmlParser::Parse(const std::string& chunk) {	
		if (_badparser) return PARSER_BAD_PARSER;
        if (_need_to_clear) {            
            _clear();
        }
		if (_c_txt == NULL) {			
			_c_txt = xmlCreatePushParserCtxt(&_sax_hdl, NULL, NULL, 0, NULL);
			if ( NULL != _c_txt)	_c_txt->_private = this;
			if ( NULL == _c_txt) return PARSER_CANT_CREATE;						 
		}	

#ifdef XMLPARSER_CPP_DEBUG
		printf("_node=%d, chunk=%s, chunklength=%d\n", _node, chunk.c_str(), chunk.length());
#endif		
		xmlParseChunk(_c_txt, chunk.c_str(), chunk.length(), 0);					

		return PARSER_NO_ERROR;
	}

	void XmlParser::_stack_clear() {
		_sp = 0;
	}

/////////////////////////////////////////////////////////////////////
	void XmlParser::On_Characters(void *ctx, const xmlChar *ch, int len) {
#ifdef XMLPARSER_CPP_DEBUG
		printf("On_Characters\n");
#endif
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		if (_p->_depth > 1)
			_p->_curEle->SetContent(std::string(reinterpret_cast<const char*>(ch),  len));
	}

	void XmlParser::On_Start_Element(void * ctx, const xmlChar * name, const xmlChar ** atts){
#ifdef XMLPARSER_CPP_DEBUG
		printf("On_Start_Element\n");
#endif
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		if (_p->_badparser) return;
		_p->_depth++;
		
		
		//stream node
		if (1 == _p->_depth || 2 == _p->_depth) {
			if (!_p->_node_send_out && _p->_node)
				delete _p->_node;
			_p->_node = new XmlNode(_local_name(name));
			_p->_node_send_out = false;
			_p->_curEle = _p->_node->GetElement();
		}
		else {
			ElementPtr tmp = _p->_curEle;
			_p->_curEle = tmp->AddChild(_local_name(name));
			_p->_stack_push(tmp);	
		}
		for (; atts && *atts ; atts += 2) {
			//namespace declaration
			if (_start_with_xmlns(*atts)) {			
				if (xmlStrlen(*atts) == 5) {				
					_p->_curEle->DeclNameSpace(std::string(reinterpret_cast<const char*>(*(atts+1))), std::string(""));
				}
				else {							
					_p->_curEle->DeclNameSpace(std::string(reinterpret_cast<const char*>(*(atts+1))), _local_name(*atts));
				}				
				continue;
		    }				
			_p->_curEle->SetAttrValue(reinterpret_cast<const char*>(*atts), reinterpret_cast<const char*>(*(atts+1)));
		}
		if (_prefix(name).length() != 0)
			_p->_curEle->SetNameSpace(_prefix(name));

		if (1 == _p->_depth) {
			_p->_root = _p->_node;
			_p->_node = new XmlNode(*(_p->_root));
			_p->_send_node();
		}
	}

	void XmlParser::On_End_Element(void* ctx, const xmlChar* name) {
#ifdef XMLPARSER_CPP_DEBUG
		printf("On_End_Element\n");
#endif
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		if (_p->_badparser) return;
		_p->_depth--;

		if (0 == _p->_depth) {
			return;
		}

		if (1 == _p->_depth)  {			
			if (_p->_notif) {
				_p->_send_node();
			}
		}
		else 	{			
			_p->_curEle = _p->_stack_pop();
		}
	}

	void XmlParser::On_Error(void* ctx, const char* fmt, ...) {
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		_p->_badparser =  true;
		printf("BadParser!!!\n");
	}
	void XmlParser::On_Warning(void* ctx, const char* fmt, ...) {
		printf("XmlParser warning!!");
	}

	std::string XmlParser::_prefix(const xmlChar* name) {		
		if (NULL == name) return "";
		std::string s(reinterpret_cast<const char *>(name));
		std::size_t found = s.find_last_of(':');
		if  (std::string::npos == found) return "";
		
		s.erase(found);
		return s;
	}

	void XmlParser::On_CdataBlock(void * ctx, const xmlChar * value, int len) {
		printf("On_CdataBlock\n");
	}
	std::string XmlParser::_local_name(const xmlChar* name) {		
		if (NULL == name) return "";
		std::string s(reinterpret_cast<const char*>(name));
		std::size_t found = s.find_last_of(':');
		if (std::string::npos == found) return s;

		s.erase(0, found+1);
		return s;
	}

	bool XmlParser::_start_with_xmlns(const xmlChar* s) {
		if (NULL == s) return false;
		return s[0] == 'x' 
			&& s[1] == 'm'
			&& s[2] == 'l'
			&& s[3] == 'n'
			&& s[4] == 's';
	}

	ElementPtr XmlParser::_stack_pop() {assert(_sp > 0); return _stack[--_sp];}
	void XmlParser::_stack_push(ElementPtr &e) {_stack[_sp++] = e;}

	void XmlParser::_send_node() {
		if (_notif) {
			_notif(_node, _notif_extra);					
			_node_send_out = true;
			_node = NULL;
		}
	}


	
} 